#ifndef __PX_PAD_LOG_H__
#define __PX_PAD_LOG_H__

#include "PadLogPoller.h"
#include <stdio.h>
#include <stdlib.h>

/*
 * *
 这是个运行主流程无锁的异步日志 默认支持32个线程
 如果写入的日志超过了设置的日志buf长度 会一直循环等待
 所以注意日志的长度需要<=日志buf的长度 默认日志buf大小是2MB
 每个写入线程一个日志buf 线程写入时不会有竞争
 使用{}进行参数捕获
 * *
*/

namespace PadLog
{
constexpr int32_t FileSize = 512 * 1024 * 1024;

enum LogLvl
{
    Pl_All = 0,
    Pl_Debug,
    Pl_Info,
    Pl_Warn,
    Pl_Error,
    Pl_Mark,
};

//一个线程对应一个buffer
//static thread_local CPadLogBuffer* pBuff = NULL;
static thread_local uint32_t ptid = 0;

template <typename LOCK = EmptyLock, int LogCnt = ThrdCnt>
class CPadLog
{
    //! 驱动器
    CPadLogPoller<LOCK>* m_log_poller;
    //! 最多支持的日志文件
    CLog* m_logs[LogCnt];
    //! buff长度 2^n
    int32_t m_buff_size = 0;
public:
    CPadLog(int buff_size = DefBuffSize) : m_log_poller(NULL), m_buff_size(buff_size)
    {
        memset(m_logs, 0, sizeof(m_logs));
        m_log_poller = new CPadLogPoller<LOCK>([this]() {this->sync(); });
    }
    ~CPadLog()
    {
        if (m_log_poller)
        {
            delete m_log_poller;
        }
        for (int i = 0; i < LogCnt; ++i)
        {
            if (m_logs[i])
            {
                m_logs[i]->close();
                delete m_logs[i];
            }
        }
    }
    CPadLogPoller<LOCK>* poller() { return m_log_poller; }
    void setBufsize(int32_t buf_size) { m_buff_size = buf_size; }
public:
    int32_t init(const char* fileName, const char* path, int32_t lv, int32_t fileSize = FileSize, int32_t fileNum = NoDateLogNum, int32_t date = 0, int32_t rotate = 1)
    {
        if (!fileName)
        {
            return -1;
        }
        CLog* log = new CLog;
        int32_t log_id = log->init(fileName, path, lv, fileSize, fileNum, date, rotate);
        if (-1 == log_id)
        {
            delete log;
            printf("CPadLog::init, file is %s, Failed\n", fileName);
            return -1;
        }
        m_logs[log_id] = log;
        return log_id;
    }

    void sync()
    {
        for (int i = 0; i < LogCnt; ++i)
        {
            if (m_logs[i])
            {
                m_logs[i]->sync();
            }
        }
    }


public:
    CPadLogBuffer<LOCK>* check(int32_t log_id, int32_t lv, uint32_t& tid)
    {
        static thread_local CPadLogBuffer<LOCK>* pBuff = NULL;
        if (!pBuff)
        {
            m_log_poller->addBuffer(&pBuff, m_buff_size, [this](int32_t log_id) { return this->m_logs[log_id]; });

        }
        if (0 == ptid)
        {
#ifdef _WIN32
            ptid = ::GetCurrentThreadId();
#else
            ptid = (pid_t)syscall(SYS_gettid);
#endif
        }
        tid = ptid;
        if (lv < m_logs[log_id]->m_lv)
        {
            return NULL;
        }
        return pBuff;
    }

    template<int N> struct Num {enum {value = N == 0 ? 1 : N};};

    template <int FormatNum, typename ...Ts>
    void writeLog(int32_t log_id, int32_t lv, const char(&Format)[FormatNum], Ts&&... Params)
    {
        uint32_t tid = 0;
        CPadLogBuffer<LOCK>* buf = check(log_id, lv, tid);
        if (buf)
        {
            //写到缓存区
            Param_Type t_params[Num<sizeof...(Ts)>::value];
            int32_t args_length = PadConv::_ArgSize_<0>(t_params, std::forward<Ts>(Params)...);
            buf->lock();
            buf->WriteLog(log_id, lv, tid, args_length, Format, t_params, std::forward<Ts>(Params)...);
            buf->unlock();
        }
    }
};

//如果全局就一个日志对象 --- 默认空锁==无锁
template <typename LOCK = EmptyLock>
class CGlobPadLog_
{
    CPadLog<LOCK> m_log;
    int32_t m_log_id = 0;
private:
    CGlobPadLog_()
    {
    }
public:
    static CGlobPadLog_& ins()
    {
        static CGlobPadLog_ log;
        return log;
    }
    int32_t init(const char* fileName, const char* path = "", int32_t lv = Pl_All, int32_t bufSize = DefBuffSize, int32_t fileSize = FileSize, int32_t fileNum = NoDateLogNum, int32_t date = 0, int32_t rotate = 1)
    {
        m_log.setBufsize(bufSize);
        m_log_id = m_log.init(fileName, path, lv, fileSize, fileNum, date, rotate);
        return m_log_id;
    }
    //
    template<int Num, typename ...Ts>
    void all(const char(&Format)[Num], Ts&&... Params)
    {
        m_log.writeLog(m_log_id, Pl_All, Format, Params...);
    }

    template<int Num, typename ...Ts>
    void debug(const char(&Format)[Num], Ts&&... Params)
    {
        m_log.writeLog(m_log_id, Pl_Debug, Format, Params...);
    }

    template<int Num, typename ...Ts>
    void info(const char(&Format)[Num], Ts&&... Params)
    {
        m_log.writeLog(m_log_id, Pl_Info, Format, Params...);
    }

    template<int Num, typename ...Ts>
    void warn(const char(&Format)[Num], Ts&&... Params)
    {
        m_log.writeLog(m_log_id, Pl_Warn, Format, Params...);
    }

    template<int Num, typename ...Ts>
    void error(const char(&Format)[Num], Ts&&... Params)
    {
        m_log.writeLog(m_log_id, Pl_Error, Format, Params...);
    }

    template<int Num, typename ...Ts>
    void mark(const char(&Format)[Num], Ts&&... Params)
    {
        m_log.writeLog(m_log_id, Pl_Mark, Format, Params...);
    }
};

class CGlobPadLog : public CGlobPadLog_<>
{
};

//这些只用于单例
inline void init(const char* fileName, const char* path = "", int32_t lv = Pl_All, int32_t bufSize = DefBuffSize, int32_t fileSize = FileSize, int32_t fileNum = NoDateLogNum, int32_t date = 0, int32_t rotate = 1)
{
    CGlobPadLog::ins().init(fileName, path, lv, bufSize, fileSize, fileNum, date, rotate);
}

template<int Num, typename ...Ts>
inline void all(const char(&Format)[Num], Ts&&... Params)
{
    CGlobPadLog::ins().all(Format, std::forward<Ts>(Params)...);
}

template<int Num, typename ...Ts>
inline  void debug(const char(&Format)[Num], Ts&&... Params)
{
    CGlobPadLog::ins().debug(Format, std::forward<Ts>(Params)...);
}

template<int Num, typename ...Ts>
inline void info(const char(&Format)[Num], Ts&&... Params)
{
    CGlobPadLog::ins().info(Format, std::forward<Ts>(Params)...);
}

template<int Num, typename ...Ts>
inline void warn(const char(&Format)[Num], Ts&&... Params)
{
    CGlobPadLog::ins().warn(Format, std::forward<Ts>(Params)...);
}

template<int Num, typename ...Ts>
inline void error(const char(&Format)[Num], Ts&&... Params)
{
    CGlobPadLog::ins().error(Format, std::forward<Ts>(Params)...);
}

template<int Num, typename ...Ts>
inline void mark(const char(&Format)[Num], Ts&&... Params)
{
    CGlobPadLog::ins().mark(Format, std::forward<Ts>(Params)...);
}

//单例: 多线程时 buf池满了后的 buf复用 --- 模板参数是锁
template <typename LOCK = std::mutex>
class CGlobLkPadLog
{
public:
    static void init(const char* fileName, const char* path = "", int32_t lv = Pl_All, int32_t bufSize = DefBuffSize, int32_t fileSize = FileSize, int32_t fileNum = NoDateLogNum, int32_t date = 0, int32_t rotate = 1)
    {
        CGlobPadLog_<LOCK>::ins().init(fileName, path, lv, bufSize, fileSize, fileNum, date, rotate);
    }

    template<int Num, typename ...Ts>
    static void all(const char(&Format)[Num], Ts&&... Params)
    {
        CGlobPadLog_<LOCK>::ins().all(Format, std::forward<Ts>(Params)...);
    }

    template<int Num, typename ...Ts>
    static void debug(const char(&Format)[Num], Ts&&... Params)
    {
        CGlobPadLog_<LOCK>::ins().debug(Format, std::forward<Ts>(Params)...);
    }

    template<int Num, typename ...Ts>
    static void info(const char(&Format)[Num], Ts&&... Params)
    {
        CGlobPadLog_<LOCK>::ins().info(Format, std::forward<Ts>(Params)...);
    }

    template<int Num, typename ...Ts>
    static void warn(const char(&Format)[Num], Ts&&... Params)
    {
        CGlobPadLog_<LOCK>::ins().warn(Format, std::forward<Ts>(Params)...);
    }

    template<int Num, typename ...Ts>
    static void error(const char(&Format)[Num], Ts&&... Params)
    {
        CGlobPadLog_<LOCK>::ins().error(Format, std::forward<Ts>(Params)...);
    }

    template<int Num, typename ...Ts>
    static void mark(const char(&Format)[Num], Ts&&... Params)
    {
        CGlobPadLog_<LOCK>::ins().mark(Format, std::forward<Ts>(Params)...);
    }
};
}

#endif
